/*
 *  Copyright 2018 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

package org.webrtc;

import androidx.annotation.Nullable;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * Implementation of RefCounted that executes a Runnable once the ref count reaches zero.
 */
class RefCountDelegate implements RefCounted {
    private final AtomicInteger refCount = new AtomicInteger(1);
    private final @Nullable
    Runnable releaseCallback;

    /**
     * @param releaseCallback Callback that will be executed once the ref count reaches zero.
     */
    public RefCountDelegate(@Nullable Runnable releaseCallback) {
        this.releaseCallback = releaseCallback;
    }

    @Override
    public void retain() {
        int updated_count = refCount.incrementAndGet();
        if (updated_count < 2) {
            throw new IllegalStateException("retain() called on an object with refcount < 1");
        }
    }

    @Override
    public void release() {
        int updated_count = refCount.decrementAndGet();
        if (updated_count < 0) {
            throw new IllegalStateException("release() called on an object with refcount < 1");
        }
        if (updated_count == 0 && releaseCallback != null) {
            releaseCallback.run();
        }
    }

    /**
     * Tries to retain the object. Can be used in scenarios where it is unknown if the object has
     * already been released. Returns true if successful or false if the object was already released.
     */
    boolean safeRetain() {
        int currentRefCount = refCount.get();
        while (currentRefCount != 0) {
            if (refCount.weakCompareAndSet(currentRefCount, currentRefCount + 1)) {
                return true;
            }
            currentRefCount = refCount.get();
        }
        return false;
    }
}
